home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / rcs-5601.lha / rcs5.6.0.1 / src / maketime.c < prev    next >
C/C++ Source or Header  |  1991-11-22  |  9KB  |  345 lines

  1. #
  2. /*
  3.  * MAKETIME        derive 32-bit time value from TM structure.
  4.  *
  5.  * Usage:
  6.  *    int zone;    Minutes west of GMT, or
  7.  *            48*60 for localtime
  8.  *    time_t t;
  9.  *    struct tm *tp;    Pointer to TM structure from <time.h>
  10.  *    t = maketime(tp,zone);
  11.  *
  12.  * Returns:
  13.  *    -1 if failure; parameter out of range or nonsensical.
  14.  *    else time-value.
  15.  * Notes:
  16.  *    This code is quasi-public; it may be used freely in like software.
  17.  *    It is not to be sold, nor used in licensed software without
  18.  *    permission of the author.
  19.  *    For everyone's benefit, please report bugs and improvements!
  20.  *     Copyright 1981 by Ken Harrenstien, SRI International.
  21.  *    (ARPANET: KLH @ SRI)
  22.  */
  23. /* $Log: maketime.c,v $
  24.  * Revision 5.3  1991/08/19  03:13:55  eggert
  25.  * Add setfiledate, str2time, TZ_must_be_set.
  26.  *
  27.  * Revision 5.2  1990/11/01  05:03:30  eggert
  28.  * Remove lint.
  29.  *
  30.  * Revision 5.1  1990/10/04  06:30:13  eggert
  31.  * Calculate the GMT offset of 'xxx LT' as of xxx, not as of now.
  32.  * Don't assume time_t is 32 bits.  Fix bugs near epoch and near end of time.
  33.  *
  34.  * Revision 5.0  1990/08/22  08:12:38  eggert
  35.  * Switch to GMT and fix the bugs exposed thereby.
  36.  * Permit dates past 1999/12/31.  Ansify and Posixate.
  37.  *
  38.  * Revision 1.8  88/11/08  13:54:53  narten
  39.  * allow negative timezones (-24h <= x <= 24h)
  40.  * 
  41.  * Revision 1.7  88/08/28  14:47:52  eggert
  42.  * Allow cc -R.  Remove unportable "#endif XXX"s.
  43.  * 
  44.  * Revision 1.6  87/12/18  17:05:58  narten
  45.  * include rcsparam.h
  46.  * 
  47.  * Revision 1.5  87/12/18  11:35:51  narten
  48.  * maketime.c: fixed USG code - you have tgo call "tzset" in order to have
  49.  * "timezone" set. ("localtime" calls it, but it's probably better not to 
  50.  * count on "localtime" having been called.)
  51.  * 
  52.  * Revision 1.4  87/10/18  10:26:57  narten
  53.  * Updating version numbers. Changes relative to 1.0 are actually 
  54.  * relative to 1.2
  55.  * 
  56.  * Revision 1.3  87/09/24  13:58:45  narten
  57.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  58.  * warnings)
  59.  * 
  60.  * Revision 1.2  87/03/27  14:21:48  jenkins
  61.  * Port to suns
  62.  * 
  63.  * Revision 1.2  83/12/05  10:12:56  wft
  64.  * added cond. compilation for USG Unix; long timezone;
  65.  * 
  66.  * Revision 1.1  82/05/06  11:38:00  wft
  67.  * Initial revision
  68.  * 
  69.  */
  70.  
  71.  
  72. #include "rcsbase.h"
  73.  
  74. libId(maketId, "$Id: maketime.c,v 5.3 1991/08/19 03:13:55 eggert Exp $")
  75.  
  76. static struct tm const *time2tm P((time_t));
  77.  
  78. #define given(v) (0 <= (v)) /* Negative values are unspecified. */
  79.  
  80. static int const daytb[] = {
  81.     /* # days in year thus far, indexed by month (0-12!!) */
  82.     0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
  83. };
  84.  
  85.     static time_t
  86. maketime(atm,zone)
  87.     struct tm const *atm;
  88.     int zone;
  89. {
  90.     register struct tm const *tp;
  91.     register int i;
  92.     int year, yday, mon, day, hour, min, sec, leap, localzone;
  93.     int attempts;
  94.     time_t t, tres;
  95.  
  96.     attempts = 2;
  97.     localzone = zone==48*60;
  98.     tres = -1;
  99.     year = mon = day = 0;  /* Keep lint happy.  */
  100.  
  101.     do {
  102.  
  103.     if (localzone || !given(atm->tm_year)) {
  104.         if (tres == -1)
  105.             if ((tres = time((time_t*)0))  ==  -1)
  106.                 return -1;
  107.         tp = time2tm(tres);
  108.         /* Get breakdowns of default time, adjusting to zone. */
  109.         year = tp->tm_year;        /* Use to set up defaults */
  110.         yday = tp->tm_yday;
  111.         mon = tp->tm_mon;
  112.         day = tp->tm_mday;
  113.         hour = tp->tm_hour;
  114.         min = tp->tm_min;
  115.         if (localzone) {
  116.             tp = localtime(&tres);
  117.             zone =
  118.             min - tp->tm_min + 60*(
  119.                 hour - tp->tm_hour + 24*(
  120.                     /* If years differ, it's by one day. */
  121.                         year - tp->tm_year
  122.                     ?    year - tp->tm_year
  123.                     :    yday - tp->tm_yday));
  124.         }
  125.         /* Adjust the default day, month and year according to zone.  */
  126.         if ((min -= zone) < 0) {
  127.             if (hour-(59-min)/60 < 0  &&  --day <= 0) {
  128.             if (--mon < 0) {
  129.                 --year;
  130.                 mon = 11;
  131.             }
  132.             day  =  daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3));
  133.             }
  134.         } else
  135.             if (
  136.               24 <= hour+min/60  &&
  137.               daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3))  <  ++day
  138.             ) {
  139.                 if (11 < ++mon) {
  140.                     ++year;
  141.                     mon = 0;
  142.                 }
  143.                 day = 1;
  144.             }
  145.     }
  146.     if (zone < -24*60  ||  24*60 < zone)
  147.         return -1;
  148.  
  149.  
  150. #ifdef DEBUG
  151. printf("first YMD: %d %d %d\n",year,mon,day);
  152. #endif
  153.     tp = atm;
  154.  
  155.     /* First must find date, using specified year, month, day.
  156.      * If one of these is unspecified, it defaults either to the
  157.      * current date (if no more global spec was given) or to the
  158.      * zero-value for that spec (i.e. a more global spec was seen).
  159.      * Reject times that do not fit in time_t,
  160.      * without assuming that time_t is 32 bits or is signed.
  161.      */
  162.     if (given(tp->tm_year))
  163.       {
  164.         year = tp->tm_year;
  165.         mon = 0;        /* Since year was given, default */
  166.         day = 1;        /* for remaining specs is zero */
  167.       }
  168.     if (year < 69)            /* 1969/12/31 OK in some timezones.  */
  169.         return -1;        /* ERR: year out of range */
  170.     leap   =   !(year&3)  &&  (year%100 || !((year+300)%400));
  171.     year -= 70;            /* UNIX time starts at 1970 */
  172.  
  173.     /*
  174.      * Find day of year.
  175.      */
  176.     {
  177.         if (given(tp->tm_mon))
  178.           {    mon = tp->tm_mon;    /* Month was specified */
  179.             day = 1;        /* so set remaining default */
  180.           }
  181.         if (11 < (unsigned)mon)
  182.             return -1;        /* ERR: bad month */
  183.         if (given(tp->tm_mday)) day = tp->tm_mday;
  184.         if(day < 1
  185.          || (((daytb[mon+1]-daytb[mon]) < day)
  186.             && (day!=29 || mon!=1 || !leap) ))
  187.                 return -1;    /* ERR: bad day */
  188.         yday = daytb[mon]    /* Add # of days in months so far */
  189.           + ((leap        /* Leap year, and past Feb?  If */
  190.               && mon>1)? 1:0)    /* so, add leap day for this year */
  191.           + day-1;        /* And finally add # days this mon */
  192.  
  193.     }
  194.     if (leap+365 <= (unsigned)yday)
  195.         return -1;        /* ERR: bad YDAY */
  196.  
  197.     if (year < 0) {
  198.         if (yday != 364)
  199.         return -1;        /* ERR: too early */
  200.         t = -1;
  201.     } else {
  202.         tres = year*365;        /* Get # days of years so far */
  203.         if (tres/365 != year)
  204.             return -1;        /* ERR: overflow */
  205.         t = tres
  206.         + ((year+1)>>2)        /* plus # of leap days since 1970 */
  207.         + yday;            /* and finally add # days this year */
  208.         if (t+4 < tres)
  209.             return -1;        /* ERR: overflow */
  210.     }
  211.     tres = t;
  212.  
  213.     if (given(i = tp->tm_wday)) /* Check WDAY if present */
  214.         if (i != (tres+4)%7)    /* 1970/01/01 was Thu = 4 */
  215.             return -1;    /* ERR: bad WDAY */
  216.  
  217. #ifdef DEBUG
  218. printf("YMD: %d %d %d, T=%ld\n",year,mon,day,tres);
  219. #endif
  220.     /*
  221.      * Now determine time.  If not given, default to zeros
  222.      * (since time is always the least global spec)
  223.      */
  224.     tres *= 86400L;            /* Get # seconds (24*60*60) */
  225.     if (tres/86400L != t)
  226.         return -1;        /* ERR: overflow */
  227.     hour = min = sec = 0;
  228.     if (given(tp->tm_hour)) hour = tp->tm_hour;
  229.     if (given(tp->tm_min )) min  = tp->tm_min;
  230.     if (given(tp->tm_sec )) sec  = tp->tm_sec;
  231.     if (60 <= (unsigned)min  ||  60 < (unsigned)sec)
  232.         return -1;        /* ERR: MS out of range */
  233.     if (24 <= (unsigned)hour)
  234.         if(hour != 24 || (min+sec) !=0)    /* Allow 24:00 */
  235.             return -1;    /* ERR: H out of range */
  236.  
  237.     t = tres;
  238.     tres += sec + 60L*(zone + min + 60*hour);
  239.  
  240. #ifdef DEBUG
  241. printf("HMS: %d %d %d T=%ld\n",hour,min,sec,tres);
  242. #endif
  243.  
  244.     if (!localzone)            /* check for overflow */
  245.         return (year<0 ? (tres<0||86400L<=tres) : tres<t)  ?  -1  :  tres;
  246.  
  247.     /* Check results; LT may have had a different GMT offset back then.  */
  248.     tp = localtime(&tres);
  249.     if (given(atm->tm_sec)  &&  atm->tm_sec != tp->tm_sec)
  250.         return -1; /* If seconds don't match, we're in trouble.  */
  251.     if (!(
  252.         given(atm->tm_min)  &&  atm->tm_min != tp->tm_min  ||
  253.         given(atm->tm_hour)  &&  atm->tm_hour != tp->tm_hour  ||
  254.         given(atm->tm_mday)  &&  atm->tm_mday != tp->tm_mday  ||
  255.         given(atm->tm_mon)  &&  atm->tm_mon != tp->tm_mon  ||
  256.         given(atm->tm_year)  &&  atm->tm_year != tp->tm_year
  257.     ))
  258.         return tres; /* Everything matches.  */
  259.  
  260.     } while (--attempts);
  261.  
  262.     return -1;
  263. }
  264.  
  265. /*
  266. * Convert Unix time to struct tm format.
  267. * Use Coordinated Universal Time (UTC) if version 5 or newer;
  268. * use local time otherwise.
  269. */
  270.     static struct tm const *
  271. time2tm(unixtime)
  272.     time_t unixtime;
  273. {
  274.     struct tm const *tm;
  275. #    if TZ_must_be_set
  276.         static char const *TZ;
  277.         if (!TZ  &&  !(TZ = getenv("TZ")))
  278.             faterror("TZ is not set");
  279. #    endif
  280.     if (!(tm  =  (RCSversion<VERSION(5) ? localtime : gmtime)(&unixtime)))
  281.         faterror("UTC is not available; perhaps TZ is not set?");
  282.     return tm;
  283. }
  284.  
  285. /*
  286. * Convert Unix time to RCS format.
  287. * For compatibility with older versions of RCS,
  288. * dates before AD 2000 are stored without the leading "19".
  289. */
  290.     void
  291. time2date(unixtime,date)
  292.     time_t unixtime;
  293.     char date[datesize];
  294. {
  295.     register struct tm const *tm = time2tm(unixtime);
  296.     VOID sprintf(date, DATEFORM,
  297.         tm->tm_year  +  (tm->tm_year<100 ? 0 : 1900),
  298.         tm->tm_mon+1, tm->tm_mday,
  299.         tm->tm_hour, tm->tm_min, tm->tm_sec
  300.     );
  301. }
  302.  
  303.  
  304.  
  305.     static time_t
  306. str2time(source)
  307.     char const *source;
  308. /* Parse a free-format date in SOURCE, yielding a Unix format time.  */
  309. {
  310.     int zone;
  311.     time_t unixtime;
  312.     struct tm parseddate;
  313.  
  314.     if (!partime(source, &parseddate, &zone))
  315.         faterror("can't parse date/time: %s", source);
  316.     if ((unixtime = maketime(&parseddate, zone))  ==  -1)
  317.         faterror("bad date/time: %s", source);
  318.     return unixtime;
  319. }
  320.  
  321.     void
  322. str2date(source, target)
  323.     char const *source;
  324.     char target[datesize];
  325. /* Parse a free-format date in SOURCE, convert it
  326.  * into RCS internal format, and store the result into TARGET.
  327.  */
  328. {
  329.     time2date(str2time(source), target);
  330. }
  331.  
  332.     int
  333. setfiledate(file, date)
  334.     char const *file, date[datesize];
  335. /* Set the access and modification time of FILE to DATE.  */
  336. {
  337.     static struct utimbuf times; /* static so unused fields are zero */
  338.     char datebuf[datesize];
  339.  
  340.     if (!date)
  341.         return 0;
  342.     times.actime = times.modtime = str2time(date2str(date, datebuf));
  343.     return utime(file, ×);
  344. }
  345.